using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using UnityEngine;

// TODO 使用接口解耦
// using Firebase.Crashlytics;

namespace LocalizationUtils.Prefs
{
    public class BinaryFilePreferences : IPreferences
    {
// #if UNITY_EDITOR || UNITY_STANDALONE
//         protected static readonly string DEFAULT_FILE = Directory.GetCurrentDirectory() + "/prefs.dat";
// #else
//         protected static readonly string DEFAULT_FILE = Application.persistentDataPath + "/prefs.dat";
// #endif
        protected static readonly string DEFAULT_FILE = Application.persistentDataPath + "/prefs.dat";
        protected readonly Dictionary<string, string> _entrys = new Dictionary<string, string>();

        protected RijndaelManaged _cipher;
        protected ICryptoTransform _encryptor;
        protected ICryptoTransform _decryptor;

        protected string _file;

        protected static readonly byte[] DEFAULT_IV = Encoding.ASCII.GetBytes("3CsM3tcL8yJKiWlX");
        protected static readonly byte[] DEFAULT_KEY = Encoding.ASCII.GetBytes("S63mndMynlTJxPn1");

        public BinaryFilePreferences() : this(DEFAULT_FILE)
        {
        }

        public BinaryFilePreferences(string file)
        {
            if (file == null)
                throw new ArgumentNullException();
            _file = file;
            _cipher = new RijndaelManaged()
            {
                Mode = CipherMode.CBC,
                Padding = PaddingMode.PKCS7,
                KeySize = 128,
                BlockSize = 128,
                FeedbackSize = 128
            };
            _encryptor = _cipher.CreateEncryptor(DEFAULT_KEY, DEFAULT_IV);
            _decryptor = _cipher.CreateDecryptor(DEFAULT_KEY, DEFAULT_IV);
            Load();
        }

        protected byte[] Encode(byte[] data)
        {
            return _encryptor.TransformFinalBlock(data, 0, data.Length);
        }

        protected byte[] Decode(byte[] data)
        {
            return _decryptor.TransformFinalBlock(data, 0, data.Length);
        }

        public void DeleteAll()
        {
            _entrys.Clear();
            if (File.Exists(_file))
            {
                File.Delete(_file);
            }
        }

        public void DeleteKey(string key)
        {
            if (_entrys.ContainsKey(key))
            {
                _entrys.Remove(key);
            }
        }

        public float GetFloat(string key)
        {
            return GetFloat(key, 0.0f);
        }

        public float GetFloat(string key, float defaultValue)
        {
            string value;
            if (_entrys.TryGetValue(key, out value))
            {
                return float.Parse(value);
            }

            return defaultValue;
        }

        public int GetInt(string key)
        {
            return GetInt(key, 0);
        }

        public int GetInt(string key, int defaultValue)
        {
            string value;
            if (_entrys.TryGetValue(key, out value))
            {
                return int.Parse(value);
            }

            return defaultValue;
        }

        public long GetLong(string key, long defaultValue)
        {
            string value;
            if (_entrys.TryGetValue(key, out value))
            {
                return long.Parse(value);
            }

            return defaultValue;
        }

        public long GetLong(string key)
        {
            return GetLong(key, 0);
        }

        public string GetString(string key)
        {
            return GetString(key, string.Empty);
        }

        public string GetString(string key, string defaultValue)
        {
            string value;
            if (_entrys.TryGetValue(key, out value))
            {
                return value;
            }

            return defaultValue;
        }

        public void Save()
        {
            Directory.CreateDirectory(Path.GetDirectoryName(_file));
            using (MemoryStream stream = new MemoryStream())
            {
                using (BinaryWriter writer = new BinaryWriter(stream))
                {
                    writer.Write(_entrys.Count);
                    foreach (var kv in _entrys)
                    {
                        writer.Write(kv.Key);
                        writer.Write(kv.Value);
                    }

                    writer.Flush();
                }

                byte[] data = stream.ToArray();
                data = Encode(data);
                File.WriteAllBytes(_file, data);
            }
        }

        public void SetFloat(string key, float value)
        {
            if (_entrys.ContainsKey(key))
            {
                _entrys.Remove(key);
            }

            _entrys[key] = Convert.ToString(value);
        }

        public void SetInt(string key, int value)
        {
            if (_entrys.ContainsKey(key))
            {
                _entrys.Remove(key);
            }

            _entrys[key] = Convert.ToString(value);
        }

        public void SetLong(string key, long value)
        {
            if (_entrys.ContainsKey(key))
            {
                _entrys.Remove(key);
            }

            _entrys[key] = Convert.ToString(value);
        }

        public void SetString(string key, string value)
        {
            if (value == null)
                throw new ArgumentNullException();
            if (_entrys.ContainsKey(key))
            {
                _entrys.Remove(key);
            }

            _entrys[key] = value;
        }

        protected void Load()
        {
            if (!File.Exists(_file))
                return;
            _entrys.Clear();
            try
            {
                byte[] data = File.ReadAllBytes(_file);
                data = Decode(data);
                using (MemoryStream stream = new MemoryStream(data))
                {
                    using (BinaryReader reader = new BinaryReader(stream))
                    {
                        int count = reader.ReadInt32();
                        for (int i = 0; i < count; i++)
                        {
                            string key = reader.ReadString();
                            string value = reader.ReadString();
                            _entrys[key] = value;
                        }
                    }
                }
            }
            catch (Exception e)
            {
                UnityEngine.Debug.LogError(e);
                // Crashlytics.LogException(e);
            }
        }
    }
}